/*
 * Copyright (c) 2020 Fraunhofer FOKUS and others. All rights reserved.
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contact: mosaic@fokus.fraunhofer.de
 */
package org.eclipse.mosaic.fed.application.ambassador.simulation;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.mosaic.fed.application.ambassador.ErrorRegister;
import org.eclipse.mosaic.fed.application.ambassador.simulation.mec.MecDetector;
import org.eclipse.mosaic.fed.application.app.api.MultiAccessEdgeComputingApplication;
import org.eclipse.mosaic.fed.application.app.api.os.MultiAccessEdgeComputingOperatingSystem;
import org.eclipse.mosaic.interactions.traffic.MecDetectorSubscription;
import org.eclipse.mosaic.interactions.traffic.MecDetectorUpdates;
import org.eclipse.mosaic.lib.objects.mapping.MecMapping;
import org.eclipse.mosaic.lib.objects.traffic.MecDetectorInfo;
import org.eclipse.mosaic.lib.util.scheduling.Event;

import java.util.List;

/**
 * This class represents a Multi-Access Edge Computing in the application simulator
 */
public class MultiAccessEdgeComputingUnit extends ServerUnit implements MultiAccessEdgeComputingOperatingSystem {

    private final String extName;
    private final MecDetector mecDetector;

    public MultiAccessEdgeComputingUnit(MecMapping mecMapping) {
        super(mecMapping.getName());
        setRequiredOperatingSystem(MultiAccessEdgeComputingOperatingSystem.class);

        this.extName = mecMapping.getExtName();
        this.mecDetector = new MecDetector(mecMapping.getName(), mecMapping.getArea());
        sendInteractionToRti(new MecDetectorSubscription(getSimulationTime(), mecMapping.getName(),
                mecMapping.getArea(), mecMapping.getEnableShow()));
    }

    @Override
    public String getExtName() {
        return extName;
    }

    @Override
    public MecDetector getMecDetector() {
        return mecDetector;
    }

    @Override
    public void processEvent(Event event) throws Exception {
        // never remove the preProcessEvent call
        final boolean preProcessed = super.preProcessEvent(event);

        // don't handle processed events
        if (preProcessed) {
            return;
        }

        final Object resource = event.getResource();

        // failsafe
        if (resource == null) {
            getOsLog().error("Event has no resource: {}", event);
            throw new RuntimeException(ErrorRegister.MULTI_ACCESS_EDGE_COMPUTING_NoEventResource.toString());
        }

        if (!handleEventResource(resource)) {
            getOsLog().error("Unknown event resource: {}", event);
            throw new RuntimeException(ErrorRegister.MULTI_ACCESS_EDGE_COMPUTING_UnknownEvent.toString());
        }
    }

    private boolean handleEventResource(Object resource) {
        if (resource instanceof MecDetectorUpdates) {
            MecDetectorUpdates mecDetectorUpdates = (MecDetectorUpdates) resource;
            List<MecDetectorInfo> updatedMecDetectors = mecDetectorUpdates.getUpdatedMecDetectors();

            for (MecDetectorInfo updatedMecDetector : updatedMecDetectors) {
                if (StringUtils.equals(updatedMecDetector.getName(), mecDetector.getId())) {
                    mecDetector.update(updatedMecDetector);

                    // application process
                    for (MultiAccessEdgeComputingApplication application : getApplicationsIterator(MultiAccessEdgeComputingApplication.class)) {
                        application.onMecDetectorUpdated(updatedMecDetector);
                    }
                }
            }

            return true;
        }
        return false;
    }
}
